package com.fileupload.monitor;

import com.fileupload.config.MonitorConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;

/**
 * 文件监控类，使用JDK内置的WatchService实现跨平台文件系统监控
 * 支持监控文件的创建、修改和删除事件
 * 性能优化版本，包含事件去重、批处理优化和资源管理增强
 */
public class NioFileMonitor implements FileMonitor {
    private static final Logger logger = LoggerFactory.getLogger(NioFileMonitor.class);
    
    // 监控配置参数
    private final String monitorPath;
    private final String monitorFileSuffix;
    private final int maxMonitorDepth;
    private final int maxMonitorDirCount;
    private final int scanIntervalMs;
    private final int eventBatchSize;
    private final long eventDelayMs;
    
    // 文件变更监听器
    private final CopyOnWriteArrayList<FileChangeListener> listeners = new CopyOnWriteArrayList<>();
    
    // 运行状态
    private final AtomicBoolean isRunning = new AtomicBoolean(false);
    
    // 事件处理相关
    private final LinkedBlockingQueue<FileEvent> eventQueue = new LinkedBlockingQueue<>(10000); // 限制队列大小防止内存溢出
    private ScheduledExecutorService eventProcessingExecutor;
    private Thread watcherThread;
    private WatchService watchService;
    private final ConcurrentHashMap<WatchKey, Path> watchKeys = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, Path> registeredDirectories = new ConcurrentHashMap<>(); // 使用Map提高查找效率
    private final Object registrationLock = new Object();
    
    // 性能优化 - 文件后缀匹配相关
    private final Set<String> fileSuffixes = new HashSet<>();
    private final Path rootPath; // 缓存根路径对象
    
    // 性能优化 - 事件去重
    private final ConcurrentHashMap<String, Long> recentFileModifications = new ConcurrentHashMap<>();
    private static final long MODIFICATION_THRESHOLD_MS = 500; // 500ms内的重复修改事件合并

    private final MonitorConfig monitorConfig;
    
    /**
     * 构造函数 - 从MonitorConfig和FileChangeListener创建
     */
    public NioFileMonitor(MonitorConfig config, FileChangeListener listener) {
        this.monitorConfig = config;
        this.monitorPath = config.getMonitorDir();
        this.monitorFileSuffix = config.getMonitorFileSuffix();
        this.maxMonitorDepth = config.getMaxDepth();
        this.maxMonitorDirCount = config.getMaxDirectories();
        this.scanIntervalMs = config.getIncrementalScanInterval() * 1000;
        this.eventBatchSize = config.getBatchSize();
        this.eventDelayMs = config.getBatchDelayMs();
        
        // 初始化根路径对象（缓存）
        this.rootPath = Paths.get(monitorPath).toAbsolutePath().normalize();
        
        // 初始化文件后缀集合（预解析）
        initFileSuffixes();
        
        if (listener != null) {
            this.addListener(listener);
        }
        
        // 启动定期清理过期修改记录的任务
        scheduleCleanupTask();
    }

    /**
     * 构造函数
     * @param monitorPath 监控根路径
     * @param monitorFileSuffix 监控的文件后缀，以逗号分隔
     * @param maxMonitorDepth 最大监控深度
     * @param maxMonitorDirCount 最大监控目录数
     * @param scanIntervalMs 扫描间隔(毫秒)
     * @param eventBatchSize 事件批处理大小
     * @param eventDelayMs 事件延迟处理时间(毫秒)
     */
    public NioFileMonitor(String monitorPath, String monitorFileSuffix, int maxMonitorDepth, 
                         int maxMonitorDirCount, int scanIntervalMs, int eventBatchSize, long eventDelayMs) {
        this.monitorConfig = null; // 原有构造函数中设置为null，因为没有MonitorConfig对象
        this.monitorPath = monitorPath;
        this.monitorFileSuffix = monitorFileSuffix;
        this.maxMonitorDepth = maxMonitorDepth;
        this.maxMonitorDirCount = maxMonitorDirCount;
        this.scanIntervalMs = scanIntervalMs;
        this.eventBatchSize = eventBatchSize;
        this.eventDelayMs = eventDelayMs;
        
        // 初始化根路径对象（缓存）
        this.rootPath = Paths.get(monitorPath).toAbsolutePath().normalize();
        
        // 初始化文件后缀集合（预解析）
        initFileSuffixes();
        
        // 启动定期清理过期修改记录的任务
        scheduleCleanupTask();
    }
    
    /**
     * 初始化文件后缀集合
     */
    private void initFileSuffixes() {
        if (monitorFileSuffix != null && !monitorFileSuffix.trim().isEmpty()) {
            String[] suffixArray = monitorFileSuffix.split(",");
            for (String suffix : suffixArray) {
                String trimmedSuffix = suffix.trim();
                if (!trimmedSuffix.isEmpty()) {
                    fileSuffixes.add(trimmedSuffix);
                }
            }
        }
    }
    
    /**
     * 启动定期清理过期修改记录的任务
     */
    private void scheduleCleanupTask() {
        ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
            Thread thread = new Thread(r, "FileEventCleanup");
            thread.setDaemon(true);
            return thread;
        });
        
        // 每5秒清理一次过期的修改记录
        cleanupExecutor.scheduleAtFixedRate(this::cleanupExpiredModifications, 
                5, 5, TimeUnit.SECONDS);
    }
    
    /**
     * 清理过期的修改记录
     */
    private void cleanupExpiredModifications() {
        long currentTime = System.currentTimeMillis();
        recentFileModifications.entrySet().removeIf(entry -> 
                currentTime - entry.getValue() > MODIFICATION_THRESHOLD_MS * 2);
    }

    /**
     * 添加文件变更监听器
     * @param listener 文件变更监听器
     */
    public void addListener(FileChangeListener listener) {
        if (listener != null) {
            listeners.addIfAbsent(listener);
        }
    }

    /**
     * 移除文件变更监听器
     * @param listener 文件变更监听器
     */
    public void removeListener(FileChangeListener listener) {
        listeners.remove(listener);
    }

    /**
     * 启动监控
     */
    public synchronized void start() {
        if (isRunning.get()) {
            return;
        }

        try {
            // 初始化WatchService
            watchService = FileSystems.getDefault().newWatchService();
            
            // 初始化事件处理线程池，使用更优化的参数
            eventProcessingExecutor = new ScheduledThreadPoolExecutor(1, r -> {
                Thread thread = new Thread(r, "FileEventProcessor");
                thread.setDaemon(true);
                thread.setPriority(Thread.NORM_PRIORITY);
                return thread;
            }, new ThreadPoolExecutor.DiscardPolicy()); // 避免任务堆积

            // 启动事件处理任务 - 使用更智能的批处理策略
            eventProcessingExecutor.schedule(this::processEventsOptimized, 
                    eventDelayMs, TimeUnit.MILLISECONDS);

            // 注册根目录监控
            if (!Files.exists(rootPath) || !Files.isDirectory(rootPath)) {
                throw new IOException("监控目录不存在或不是目录: " + monitorPath);
            }
            
            registerDirectoryRecursive(rootPath, 0);
            
            // 初始化扫描：检查目录中已存在的文件
            scanExistingFiles();

            // 启动监控线程
            watcherThread = new Thread(this::watchDirectory, "FileWatcher");
            watcherThread.setDaemon(true);
            watcherThread.setPriority(Thread.MAX_PRIORITY); // 提高监控线程优先级
            watcherThread.start();

            isRunning.set(true);
            logger.info("文件监控服务已启动，监控路径: {}", monitorPath);
        } catch (Exception e) {
            logger.error("启动文件监控失败: {}", e.getMessage(), e);
            stop();
        }
    }

    /**
     * 停止监控 - 增强资源清理
     */
    public synchronized void stop() {
        if (!isRunning.get()) {
            return;
        }

        isRunning.set(false);
        logger.info("正在停止文件监控服务: {}", monitorPath);

        try {
            // 取消所有监控 - 使用并行流加速取消操作
            List<WatchKey> keysToCancel = new ArrayList<>(watchKeys.keySet());
            keysToCancel.forEach(WatchKey::cancel);
            watchKeys.clear();

            // 关闭WatchService
            if (watchService != null) {
                try {
                    watchService.close();
                } catch (IOException e) {
                    logger.warn("关闭WatchService失败: {}", e.getMessage());
                }
                watchService = null;
            }

            // 等待监控线程结束
            if (watcherThread != null && watcherThread.isAlive()) {
                watcherThread.interrupt();
                try {
                    // 使用超时等待，避免死锁
                    watcherThread.join(Math.min(3000, scanIntervalMs * 2));
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    logger.warn("等待监控线程结束时被中断");
                }
                if (watcherThread.isAlive()) {
                    logger.warn("监控线程未能在超时时间内结束");
                }
                watcherThread = null;
            }

            // 关闭事件处理线程
            if (eventProcessingExecutor != null) {
                eventProcessingExecutor.shutdownNow(); // 立即停止所有任务
                try {
                    if (!eventProcessingExecutor.awaitTermination(2, TimeUnit.SECONDS)) {
                        logger.warn("事件处理线程池未能在超时时间内关闭");
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    logger.warn("等待事件处理线程池关闭时被中断");
                }
                eventProcessingExecutor = null;
            }

            // 清空所有集合和缓存，释放内存
            eventQueue.clear();
            registeredDirectories.clear();
            recentFileModifications.clear();
            listeners.clear();

            logger.info("文件监控服务已成功停止: {}", monitorPath);
        } catch (Exception e) {
            logger.error("停止文件监控服务时发生错误: {}", e.getMessage(), e);
        }
    }

    /**
     * 递归注册目录
     */
    private void registerDirectoryRecursive(Path dir, int currentDepth) throws IOException {
        if (currentDepth > maxMonitorDepth || registeredDirectories.size() >= maxMonitorDirCount) {
            return;
        }

        String dirPath = dir.toAbsolutePath().toString();
        
        // 先检查是否已注册，减少锁竞争
        if (registeredDirectories.putIfAbsent(dirPath, dir) != null) {
            return; // 已存在，不需要注册
        }

        synchronized (registrationLock) {
            // 二次检查避免竞态条件
            if (registeredDirectories.size() >= maxMonitorDirCount) {
                registeredDirectories.remove(dirPath);
                return;
            }

            try {
                // 注册目录到WatchService
                WatchKey key = dir.register(watchService, 
                        StandardWatchEventKinds.ENTRY_CREATE,
                        StandardWatchEventKinds.ENTRY_DELETE,
                        StandardWatchEventKinds.ENTRY_MODIFY);
                
                watchKeys.put(key, dir);
                
                // 递归注册子目录 - 使用并行流提高效率（对于大量子目录）
                if (currentDepth < maxMonitorDepth) {
                    try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, Files::isDirectory)) {
                        // 先收集再处理，减少锁持有时间
                        List<Path> subDirs = new ArrayList<>();
                        stream.forEach(subDirs::add);
                        
                        for (Path subDir : subDirs) {
                            try {
                                registerDirectoryRecursive(subDir, currentDepth + 1);
                            } catch (Exception e) {
                                logger.warn("注册子目录失败: {}, 错误: {}", subDir, e.getMessage());
                            }
                            if (registeredDirectories.size() >= maxMonitorDirCount) {
                                break;
                            }
                        }
                    } catch (AccessDeniedException e) {
                        logger.warn("无权限访问目录: {}", dir);
                    }
                }
            } catch (Exception e) {
                // 注册失败，清理状态
                registeredDirectories.remove(dirPath);
                throw e;
            }
        }
    }

    /**
     * 监控目录变化
     */
    private void watchDirectory() {
        while (isRunning.get()) {
            WatchKey key;
            try {
                // 等待事件发生，超时为500ms
                key = watchService.poll(500, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            } catch (ClosedWatchServiceException e) {
                break;
            }

            if (key == null) {
                continue;
            }

            // 获取发生事件的目录
            Path dir = watchKeys.get(key);
            if (dir == null) {
                key.cancel();
                continue;
            }

            // 批量收集事件，减少处理开销
            List<WatchEvent<?>> events = key.pollEvents();
            
            // 批量处理事件
            for (WatchEvent<?> event : events) {
                WatchEvent.Kind<?> kind = event.kind();

                // 特殊情况：服务被重置
                if (kind == StandardWatchEventKinds.OVERFLOW) {
                    logger.warn("文件监控事件溢出，部分事件可能丢失");
                    continue;
                }

                // 获取事件的上下文（文件名）
                @SuppressWarnings("unchecked")
                WatchEvent<Path> ev = (WatchEvent<Path>) event;
                Path fileName = ev.context();
                Path fullPath = dir.resolve(fileName);

                try {
                    handleEvent(fullPath, kind);
                } catch (Exception e) {
                    logger.warn("处理事件失败: {}, 错误: {}", fullPath, e.getMessage(), e);
                }
            }

            // 重置key，如果返回false表示该key不再有效
            boolean valid = key.reset();
            if (!valid) {
                watchKeys.remove(key);
                // 如果没有更多的监控键，退出循环
                if (watchKeys.isEmpty()) {
                    break;
                }
            }
        }
    }

    /**
     * 处理文件系统事件
     */
    private void handleEvent(Path path, WatchEvent.Kind<?> kind) {
        try {
            if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
                if (Files.isDirectory(path)) {
                    int depth = calculateDirectoryDepth(path);
                    if (depth <= maxMonitorDepth && registeredDirectories.size() < maxMonitorDirCount) {
                        try {
                            registerDirectoryRecursive(path, depth);
                        } catch (IOException e) {
                            logger.warn("注册新目录失败: {}, 错误: {}", path, e.getMessage());
                        }
                    }
                    // 目录创建事件不加入队列，因为我们只关心文件事件
                } else if (matchesFileSuffix(path) && Files.exists(path)) {
                    // 确保文件确实存在再加入队列
                    boolean added = eventQueue.offer(new FileEvent(path.toFile(), EventType.FILE_CREATED));
                    if (!added) {
                        logger.warn("事件队列已满，丢弃文件创建事件: {}", path);
                    }
                }
            } else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
                String filePathStr = path.toAbsolutePath().toString();
                // 先检查是否是目录
                if (registeredDirectories.containsKey(filePathStr)) {
                    unregisterDirectory(path);
                    // 目录删除事件不加入队列
                } else if (matchesFileSuffix(path)) {
                    // 对于删除事件，文件可能已经不存在，但我们仍需处理
                    boolean added = eventQueue.offer(new FileEvent(path.toFile(), EventType.FILE_DELETED));
                    if (!added) {
                        logger.warn("事件队列已满，丢弃文件删除事件: {}", path);
                    }
                    // 移除可能的修改记录
                    recentFileModifications.remove(filePathStr);
                }
            } else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
                if (Files.isRegularFile(path) && matchesFileSuffix(path)) {
                    // 性能优化：事件去重 - 避免短时间内重复处理相同文件的修改事件
                    String filePathStr = path.toAbsolutePath().toString();
                    long currentTime = System.currentTimeMillis();
                    Long lastModified = recentFileModifications.put(filePathStr, currentTime);
                    
                    // 如果上次修改时间与当前时间相差超过阈值，则处理事件
                    if (lastModified == null || currentTime - lastModified > MODIFICATION_THRESHOLD_MS) {
                        boolean added = eventQueue.offer(new FileEvent(path.toFile(), EventType.FILE_MODIFIED));
                        if (!added) {
                            logger.warn("事件队列已满，丢弃文件修改事件: {}", path);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.warn("处理文件系统事件异常: {}, 错误: {}", path, e.getMessage(), e);
        }
    }

    /**
     * 优化版事件处理方法 - 使用更智能的批处理策略
     */
    private void processEventsOptimized() {
        if (!isRunning.get()) {
            return;
        }

        if (eventQueue.isEmpty() || listeners.isEmpty()) {
            // 队列为空时，使用较长的延迟再次调度
            scheduleNextProcessing(eventDelayMs * 2);
            return;
        }

        // 批量处理事件，使用更高效的收集方式
        List<FileEvent> batchEvents = new ArrayList<>(Math.min(eventQueue.size(), eventBatchSize));
        
        // 一次性取出多个事件，减少锁竞争
        eventQueue.drainTo(batchEvents, eventBatchSize);

        if (batchEvents.isEmpty()) {
            scheduleNextProcessing(eventDelayMs);
            return;
        }

        // 性能优化：按文件路径和事件类型进行分组，合并短时间内的重复事件
        Map<String, FileEvent> mergedEvents = new HashMap<>(batchEvents.size());
        for (FileEvent event : batchEvents) {
            String key = event.getFile().getAbsolutePath() + ":" + event.getType();
            // 对于修改事件，保留最新的
            mergedEvents.put(key, event);
        }

        // 通知所有监听器
        for (FileEvent event : mergedEvents.values()) {
            for (FileChangeListener listener : listeners) {
                try {
                    dispatchEvent(listener, event);
                } catch (Exception e) {
                    logger.error("处理文件事件时出错: {}", e.getMessage(), e);
                }
            }
        }

        // 动态调整下一次处理的延迟时间
        // 如果队列中还有很多事件，使用较短的延迟
        long nextDelay = eventQueue.size() > eventBatchSize * 3 ? 
                Math.max(10, eventDelayMs / 2) : eventDelayMs;
        
        scheduleNextProcessing(nextDelay);
    }
    
    /**
     * 调度下一次事件处理
     */
    private void scheduleNextProcessing(long delayMs) {
        if (eventProcessingExecutor != null && !eventProcessingExecutor.isShutdown()) {
            eventProcessingExecutor.schedule(this::processEventsOptimized, 
                    delayMs, TimeUnit.MILLISECONDS);
        }
    }

    /**
     * 分发事件到监听器
     */
    private void dispatchEvent(FileChangeListener listener, FileEvent event) {
        switch (event.getType()) {
            case FILE_CREATED:
                listener.onFileCreated(event.getFile(), monitorConfig);
                break;
            case FILE_MODIFIED:
                listener.onFileModified(event.getFile(), monitorConfig);
                break;
            case FILE_DELETED:
                listener.onFileDeleted(event.getFile(), monitorConfig);
                break;
            case DIRECTORY_CREATED:
                // 目录创建事件暂时不处理
                break;
            case DIRECTORY_DELETED:
                // 目录删除事件暂时不处理
                break;
        }
    }

    /**
     * 检查文件是否匹配监控的后缀 - 优化版本
     */
    private boolean matchesFileSuffix(Path path) {
        if (path == null) {
            return false;
        }

        // 性能优化：直接使用预解析的后缀集合
        String fileName = path.getFileName().toString();
        
        // 快速路径：如果没有指定后缀，匹配所有文件
        if (fileSuffixes.isEmpty()) {
            return true;
        }
        
        // 遍历后缀集合进行匹配
        for (String suffix : fileSuffixes) {
            if (fileName.endsWith(suffix)) {
                return true;
            }
        }
        
        return false;
    }

    /**
     * 计算目录深度 - 使用缓存的根路径
     */
    private int calculateDirectoryDepth(Path dirPath) {
        try {
            // 使用缓存的根路径对象，避免重复创建
            Path absoluteDirPath = dirPath.toAbsolutePath().normalize();
            
            if (!absoluteDirPath.startsWith(rootPath)) {
                return maxMonitorDepth + 1; // 超出监控范围
            }
            
            return (int) rootPath.relativize(absoluteDirPath).getNameCount();
        } catch (Exception e) {
            logger.warn("计算目录深度失败: {}, 错误: {}", dirPath, e.getMessage());
            return maxMonitorDepth + 1;
        }
    }

    /**
     * 注销目录 - 优化版本
     */
    private void unregisterDirectory(Path dirPath) {
        // 优化锁粒度，只在必要时加锁
        String dirStr = dirPath.toAbsolutePath().toString();
        
        // 先尝试移除目录注册
        Path removedPath = registeredDirectories.remove(dirStr);
        if (removedPath == null) {
            return; // 目录未注册，无需处理
        }
        
        synchronized (registrationLock) {
            // 查找并取消对应的WatchKey - 使用更高效的方式
            watchKeys.entrySet().removeIf(entry -> {
                boolean match = entry.getValue().toAbsolutePath().toString().equals(dirStr);
                if (match) {
                    entry.getKey().cancel();
                }
                return match;
            });
            
            // 批量移除所有子目录的注册
            Set<String> pathsToRemove = new HashSet<>();
            for (String path : registeredDirectories.keySet()) {
                if (path.startsWith(dirStr + File.separator)) {
                    pathsToRemove.add(path);
                }
            }
            
            // 一次性移除所有匹配的路径，减少锁持有时间
            for (String path : pathsToRemove) {
                registeredDirectories.remove(path);
                // 同时取消对应的WatchKey
                watchKeys.entrySet().removeIf(entry -> 
                    entry.getValue().toAbsolutePath().toString().equals(path));
            }
        }
    }

    /**
     * 获取当前状态
     */
    @Override
    public boolean isRunning() {
        return isRunning.get();
    }
    
    /**
     * 获取监控配置
     */
    @Override
    public MonitorConfig getMonitorConfig() {
        return monitorConfig;
    }
    
    /**
     * 获取监控器名称
     */
    @Override
    public String getName() {
        return monitorConfig != null ? monitorConfig.getName() : "unnamed-monitor"; 
    }
    
    /**
     * 扫描目录中已存在的文件
     * 在监控服务启动时执行，处理已存在的文件
     */
    private void scanExistingFiles() {
        logger.info("开始扫描已存在的文件: {}", monitorPath);
        
        try {
            // 使用Files.walkFileTree递归扫描目录
            Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    // 检查目录深度
                    int depth = calculateDirectoryDepth(dir);
                    if (depth > maxMonitorDepth) {
                        return FileVisitResult.SKIP_SUBTREE; // 跳过超出深度的子目录
                    }
                    return FileVisitResult.CONTINUE;
                }
                
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    // 检查文件是否匹配后缀
                    if (matchesFileSuffix(file) && Files.exists(file)) {
                        // 为已存在的文件触发创建事件
                        logger.info("发现已存在文件并加入处理队列: {}", file);
                        boolean added = eventQueue.offer(new FileEvent(file.toFile(), EventType.FILE_CREATED));
                        if (!added) {
                            logger.warn("事件队列已满，丢弃已存在文件: {}", file);
                        }
                    }
                    return FileVisitResult.CONTINUE;
                }
                
                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) {
                    logger.warn("访问文件失败: {}, 错误: {}", file, exc.getMessage());
                    return FileVisitResult.CONTINUE; // 继续处理其他文件
                }
            });
            
            logger.info("已存在文件扫描完成: {}", monitorPath);
        } catch (IOException e) {
            logger.error("扫描已存在文件时发生错误: {}", e.getMessage(), e);
        }
    }

    /**
     * 事件类型枚举
     */
    public enum EventType {
        FILE_CREATED,
        FILE_MODIFIED,
        FILE_DELETED,
        DIRECTORY_CREATED,
        DIRECTORY_DELETED
    }

    /**
     * 文件事件类
     */
    public static class FileEvent {
        private final File file;
        private final EventType type;

        public FileEvent(File file, EventType type) {
            this.file = file;
            this.type = type;
        }

        public File getFile() {
            return file;
        }

        public EventType getType() {
            return type;
        }

        @Override
        public String toString() {
            return "FileEvent{type=" + type + ", file=" + file.getAbsolutePath() + "}";
        }
    }

    // 使用外部的FileChangeListener接口，不再定义内部接口
}